package astroim.samp;

import astroim.AstroIMMessaging;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.astrogrid.samp.ErrInfo;
import org.astrogrid.samp.Message;
import org.astrogrid.samp.Metadata;
import org.astrogrid.samp.Response;
import org.astrogrid.samp.client.AbstractMessageHandler;
import org.astrogrid.samp.client.ClientProfile;
import org.astrogrid.samp.client.DefaultClientProfile;
import org.astrogrid.samp.client.HubConnection;
import org.astrogrid.samp.client.HubConnector;
import org.astrogrid.samp.client.SampException;

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
/**
 *
 * @author alykhalid
 */
public class ProxyClient {

    private HubConnector connection = null;
    public String LocalID, ProxyID, Name, Description, ClientName;
    public Map RegisteredSubscriptions;
    private AstroIMMessaging aimm;
    private static final Pattern LOCALHOST_REGEX =
            Pattern.compile("(http://|ftp://)"
            + "(127\\.0\\.0\\.1|localhost)"
            + "([:/].*)");
    private static final Pattern FILE_REGEX =
            Pattern.compile("(file://)"
            + "([^/]*)"
            + "/.*");

    public ProxyClient setProxyID(String ProxyID) {
        this.ProxyID = ProxyID;
        return this;
    }

    public ProxyClient setAimm(AstroIMMessaging aimm) {
        this.aimm = aimm;
        return this;
    }

    public ProxyClient setLocalID(String ID) {
        this.LocalID = ID;
        return this;
    }

    public ProxyClient setName(String Name) {
        this.Name = Name;
        return this;
    }

    public ProxyClient setClientName(String ClientName) {
        this.ClientName = ClientName;
        return this;
    }

    public ProxyClient setDescription(String Description) {
        this.Description = Description;
        return this;
    }

    public ProxyClient setRegisteredSubscriptions(Map RegisteredSubscriptions) {
        this.RegisteredSubscriptions = RegisteredSubscriptions;
        this.RegisteredSubscriptions.remove("samp.hub.event.shutdown");
        this.RegisteredSubscriptions.remove("samp.hub.event.register");
        this.RegisteredSubscriptions.remove("samp.hub.event.unregister");
        this.RegisteredSubscriptions.remove("samp.hub.event.metadata");
        this.RegisteredSubscriptions.remove("samp.hub.event.subscriptions");
        return this;
    }

    public ProxyClient registerWithHub() throws SampException {
        ClientProfile profile = DefaultClientProfile.getProfile();
        connection = new HubConnector(profile);

        Metadata meta = new Metadata();
        meta.setName(Name + " [" + ClientName + "]");
        meta.setDescriptionText(Description);
        connection.declareMetadata(meta);
        this.setLocalID(connection.getConnection().getRegInfo().getSelfId());
        Iterator itSubscriptionMap = RegisteredSubscriptions.entrySet().iterator();
        while (itSubscriptionMap.hasNext()) {
            Map.Entry SubscriptionMapPairs = (Map.Entry) itSubscriptionMap.next();
            connection.addMessageHandler(new AbstractMessageHandler(SubscriptionMapPairs.getKey().toString()) {

                @Override
                public Map processCall(HubConnection connection, String senderId, Message message) throws Exception {

                    System.out.println("-------------------");
                    System.out.println("Name: " + Name);
                    System.out.println("Message received by: " + LocalID);
                    System.out.println("Sent by " + senderId);
                    System.out.println("ProxyID " + ProxyID);
                    System.out.println("Message MType: " + message.getMType());
                    System.out.println("Message Params: " + newline + IterateOverMap(message.getParams()));
                    System.out.println("-------------------");
                    if (ContainsLocalURL(message.getParams())) {
                        System.out.println("In conatains URL");
                        ErrInfo ei = new ErrInfo();
                        ei.setErrortxt("AstroIM can't forward the message");
                        String usertxt = new StringBuffer()
                                .append("AstroIM can't forward message to recipient.\n")
                                .append("The message conatins reference to a local file.\n")
                                .append("The file wont be transferred to the recipient.\n")
                                .append("To fix this issue, upload the file to VO space or any other publically accessible URL;\n")
                                .append("Reopen the file from publically accessible URL and try again.\n")
                                .toString();
                        ei.setUsertxt(usertxt);
                        return Response.createErrorResponse(ei);
                    } else {
                        org.jivesoftware.smack.packet.Message msg = new org.jivesoftware.smack.packet.Message(ClientName, org.jivesoftware.smack.packet.Message.Type.normal);
                        msg.setBody("@SampHub");
                        msg.setProperty("MType", message.getMType());
                        msg.setProperty("RecipientID", ProxyID);
                        msg = InsertMapIntoMessage(msg, message.getParams());
                        aimm.sendMessage(msg);
                        return message;
                    }
                }

                @Override
                public void receiveCall(HubConnection connection, String senderId,
                        String msgId, Message message)
                        throws SampException {
                    System.out.println("In ReceiveCall");
                    Response response;
                    try {
                        Map result = processCall(connection, senderId, message);
                        result = result == null ? new HashMap() : result;
                        response = Response.createSuccessResponse(result);
                    } catch (Throwable e) {
                        response = Response.createErrorResponse(new ErrInfo(e));
                    }
                    connection.reply(msgId, response);
                }
                
                private boolean ContainsLocalURL(Map params) {
                    System.out.println("Checking for Locacl URL");
                    boolean ContainsLocal = false;
                    Map<?, ?> data = (Map<?, ?>) params;
                    for (Map.Entry<?, ?> entry : data.entrySet()) {
                        if ("url".equalsIgnoreCase(entry.getKey().toString())) {
                            Matcher LOCALHOST_Matcher = LOCALHOST_REGEX.matcher(entry.getValue().toString());
                            Matcher FILE_Matcher = FILE_REGEX.matcher(entry.getValue().toString());
                            if (LOCALHOST_Matcher.matches() || FILE_Matcher.matches()) {
                                ContainsLocal = true;
                                System.out.println("It conatains Local URL");
                            }
                        }
                    }
                    return ContainsLocal;
                }
            });
        }
        Map subs = connection.computeSubscriptions();
        subs.remove("samp.hub.event.shutdown");
        subs.remove("samp.hub.event.register");
        subs.remove("samp.hub.event.unregister");
        subs.remove("samp.hub.event.metadata");
        subs.remove("samp.hub.event.subscriptions");
        System.out.println(IterateOverMap(subs));
        connection.declareSubscriptions(subs);


        return this;
    }
    private final static String newline = "\n";

    private String IterateOverMap(Map map) {
        Map<?, ?> data = (Map<?, ?>) map;
        StringBuilder sb = new StringBuilder();
        for (Map.Entry<?, ?> entry : data.entrySet()) {
            sb.append(entry.getKey());
            sb.append(": ");
            sb.append(entry.getValue());
            sb.append(newline);
        }
        return sb.toString();

    }

    private org.jivesoftware.smack.packet.Message InsertMapIntoMessage(org.jivesoftware.smack.packet.Message msg, Map map) {
        Map<?, ?> data = (Map<?, ?>) map;
        for (Map.Entry<?, ?> entry : data.entrySet()) {
            msg.setProperty(entry.getKey().toString(), entry.getValue());
        }
        return msg;
    }

    public void unregisterFromHub() throws SampException {
        connection.getConnection().unregister();
    }
}
